﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace System.ComponentModel.Composition.Hosting
{
    /// <summary>
    /// mimic of the MEF class CompositionContainer to use compatible structur for CLR creation.
    /// </summary>
    public class CompositionContainer : IDisposable
    {
        internal class TypeExplorer
        {
            public Reflection.FieldInfo ContainerField;
            public Object ContractType;
            public List<Object> DiscoveredTypes;
            public Object ContainerObject;

            public TypeExplorer()
            {
                this.DiscoveredTypes = new List<Object>();
            }

            internal void SetValue()
            {
                Type listType = null, fullNameType = null;
                Object list = null;
                ///get the full name Type for the Activator.
                fullNameType = Type.GetType(String.Concat(this.ContainerField.FieldType.Namespace, ".", this.ContainerField.FieldType.Name));
                ///If the new Type is List, so create it into listType.
                if (this.ContainerField.FieldType.Name.Contains("List") == true)
                    listType = fullNameType.MakeGenericType((Type)this.ContractType);
                if (listType == null)
                    list = Activator.CreateInstance(fullNameType, new Object[] { this.DiscoveredTypes.Count });
                else
                    list = Activator.CreateInstance(listType);
                ///Copy data into the given list or array
                if (fullNameType.Name.Contains("List") == true)
                    this.DiscoveredTypes.ForEach(a => ((IList)list).Add(a));
                else
                    this.DiscoveredTypes.CopyTo((Object[])list);

                this.ContainerField.SetValue(this.ContainerObject, list);
            }
        }
        /// <summary>
        /// Directory Catalag for scanned data.
        /// </summary>
        private DirectoryCatalog Catalog;
        /// <summary>
        /// Initialize mimic CompositionContainer of the MEF
        /// </summary>
        /// <param name="catalog">Using Directory Catalog</param>
        public CompositionContainer(DirectoryCatalog catalog)
        {
            this.Catalog = catalog;
        }
        /// <summary>
        /// mimic of the MEF ComposeParts. Write the parts into given objects.
        /// </summary>
        /// <param name="attributedParts">object that are used to writes the parts down.</param>
        public void ComposeParts(params Object[] attributedParts)
        {
            try
            {
                Object[] args = new Object[] { };
                var exportTypes = new List<TypeExplorer>();
                foreach (var obj in attributedParts)
                {
                    foreach (var field in obj.GetType().GetFields(Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Public | Reflection.BindingFlags.Instance))
                    {
                        var imports = (ImportManyAttribute[])field.GetCustomAttributes(typeof(ImportManyAttribute), true);
                        if (imports != null && imports.Length > 0)
                        {
                            exportTypes.Add(new TypeExplorer { ContainerField = field, ContractType = imports.First().ContractType, ContainerObject = obj });
                        }
                    }
                }
                foreach (var entry in this.Catalog.Files)
                {
                    var tlist = entry.GetTypes();
                    foreach (var type in tlist)
                    {
                        foreach (var value in exportTypes)
                        {
                            var cType = (ExportAttribute)type.GetCustomAttributes(typeof(ExportAttribute), true).FirstOrDefault();
                            if (cType != null)
                            {
                                var cinfo = type.GetConstructor(Type.EmptyTypes);
                                value.DiscoveredTypes.Add(cinfo.Invoke(args));
                            }
                        }
                    }
                }
                exportTypes.ForEach(a => a.SetValue());
            }
            catch (Exception e)
            {
                e.Data["LastFile"] = this.Catalog.LastLoadingFile;
                throw;
            }
        }

        #region Disposed
        /// <summary>
        /// Finalizer
        /// </summary>
        ~CompositionContainer()
        {
            this.Dispose(false);
        }
        /// <summary>
        /// Managed/Unmanaged Dispose
        /// </summary>
        /// <param name="disposing">true => managed dispose</param>
        private void Dispose(Boolean disposing)
        {
            if (disposing == true)
            {
                this.Catalog.Dispose();
            }
            this.Catalog = null;
        }
        /// <summary>
        /// Call Dispose to clean up this object.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            // Use SupressFinalize in case a subclass 
            // of this type implements a finalizer.
            GC.SuppressFinalize(this);
        }
        #endregion
    }
}
